//
//  Codable.swift
//  MoonKit
//
//  Created by moon on 2019/3/1.
//  Copyright © 2019 moon. All rights reserved.
//

import Foundation

/// 当前库版本号
let version = "1.0.0"

/// 命名空间体,用于扩展已有的类型
public struct NameSpace<T> {
    public var value:T
    public init(_ value:T) {
        self.value = value
    }
}

public typealias CodableItem = Codable//为了与Swift.Codable进行区分

public protocol Codable {
    /// 静态方法，从容器中初始出对象
    /// 如果需要初始化对象的常量属性，可以自定义init(from coder:Coder) throws 构造器，在其中进行常量属性的初始化
    /// - Parameter coder: 数据容器
    /// - Throws: CodingError中指定的错误
    static func initObject(from coder:Coder) throws -> Self

    /// 编码数据到容器中或者从容器中解码数据更新对象
    ///
    /// - Parameter coder: 数据容器
    /// - Parameter object: 被操作的对象
    /// - Throws: CodingError中指定的错误
    mutating func code(coder: Coder) throws
}

extension Array : Codable where Element : Codable {
    @inlinable
    public static func initObject(from coder: Coder) throws -> Array<Element> {
        var obj = Array<Element>.init()
        var container = try coder.unkeyedContainer()
        while !container.isAtEnd {
            let element = try container.decode(Element.self)
            obj.append(element)
        }
        return obj
    }
	@inlinable
	public mutating func code(coder: Coder) throws {
		if coder.isEncoding {
			var container = try coder.unkeyedContainer()
			try container.encode(contentsOf: self)
		} else {
			var container = try coder.unkeyedContainer()
			var array:[Element] = []
			for var value in self {
				if container.isAtEnd {
					break
				} else {
					let superCoder = try container.superCoder()
                    try value.code(coder: superCoder)
					array.append(value)
				}
			}
            while !container.isAtEnd {
                let v:Element = try container.decode(Element.self)
                array.append(v)
            }
			self = array
		}
	}
}
extension Set : Codable where Element : Codable {
    @inlinable
    public static func initObject(from coder: Coder) throws -> Set<Element> {
        var obj = Set<Element>.init()
        var container = try coder.unkeyedContainer()
        while !container.isAtEnd {
            let element = try container.decode(Element.self)
            obj.insert(element)
        }
        return obj
    }
	@inlinable
	public mutating func code(coder: Coder) throws {
		if coder.isEncoding {
			var container = try coder.unkeyedContainer()
			for element in self {
				try container.encode(element)
			}
		} else {
            self = try Self.initObject(from: coder)
		}
	}
}
extension Dictionary : Codable where Key : Codable,Value : Codable {
	@inlinable
	public static func initObject(from coder: Coder) throws -> Dictionary<Key, Value> {
        var obj = Dictionary<Key, Value>.init()
		if Key.self == String.self {
			var container = try coder.container(keyedBy: StringCodingKey.self)
			for key in container.allKeys {
				let value = try container.decode(Value.self, forKey: key)
				obj[key.stringValue as! Key] = value
			}
		} else if Key.self == Int.self {
			var container = try coder.container(keyedBy: StringCodingKey.self)
			for key in container.allKeys {
				guard key.intValue != nil else {
					// We provide stringValues for Int keys; if an encoder chooses not to
					// use the actual intValues, we've encoded string keys.
					// So on init, _DictionaryCodingKey tries to parse string keys as
					// Ints. If that succeeds, then we would have had an intValue here.
					// We don't, so this isn't a valid Int key.
					throw CodingError.typeMismatch(
						Int.self, CodingError.Context(codingPath: container.codingPath+[key],debugDescription: "Expected Int key but found String key instead."))
				}
				let value = try container.decode(Value.self, forKey: key)
				obj[key.stringValue as! Key] = value
			}
		} else {
			// We should have encoded as an array of alternating key-value pairs.
			var container = try coder.unkeyedContainer()
			let count = container.count
			guard count % 2 == 0 else {
				throw CodingError.dataCorrupted(CodingError.Context(codingPath: container.codingPath,debugDescription: "Expected collection of key-value pairs; encountered odd-length array instead."))
			}
			while !container.isAtEnd {
				let key = try container.decode(Key.self)
				
				guard !container.isAtEnd else {
					throw CodingError.dataCorrupted(CodingError.Context(codingPath: container.codingPath,debugDescription: "Unkeyed container reached end before value in key-value pair."))
				}
				let value = try container.decode(Value.self)
				obj[key] = value
			}
		}
        return obj
	}
	@inlinable
	public mutating func code(coder: Coder) throws {
		if coder.isEncoding {
			if Key.self == String.self {
				var container = try coder.container(keyedBy: StringCodingKey.self)
				for (key,value) in self {
					try container.encode(value, forKey: StringCodingKey(key as! String))
				}
			} else if Key.self == Int.self {
				var container = try coder.container(keyedBy: StringCodingKey.self)
				for (key,value) in self {
					try container.encode(value, forKey: StringCodingKey(intValue: key as! Int))
				}
			} else {
				// Keys are Encodable but not Strings or Ints, so we cannot arbitrarily
				// convert to keys. We can encode as an array of alternating key-value
				// pairs, though.
				var container = try coder.unkeyedContainer()
				for (key, value) in self {
					try container.encode(key)
					try container.encode(value)
				}
			}
		} else {
			var dictionary:[Key:Value] = [:]
			if Key.self == String.self {
				var container = try coder.container(keyedBy: StringCodingKey.self)
				for key in container.allKeys {
					if var value = self[key.stringValue as! Key] {
                        let superCoder = try container.superCoder(forKey: key)
                        try value.code(coder: superCoder)
						dictionary[key.stringValue as! Key] = value
					} else {
						let value = try container.decode(Value.self, forKey: key)
						dictionary[key.stringValue as! Key] = value
					}
				}
			} else if Key.self == Int.self {
				var container = try coder.container(keyedBy: StringCodingKey.self)
				for key in container.allKeys {
					guard key.intValue != nil else {
						// We provide stringValues for Int keys; if an encoder chooses not to
						// use the actual intValues, we've encoded string keys.
						// So on init, _DictionaryCodingKey tries to parse string keys as
						// Ints. If that succeeds, then we would have had an intValue here.
						// We don't, so this isn't a valid Int key.
						throw CodingError.typeMismatch(
							Int.self, CodingError.Context(codingPath: container.codingPath+[key],debugDescription: "Expected Int key but found String key instead."))
					}
					if var value = self[key.stringValue as! Key] {
                        let superCoder = try container.superCoder(forKey: key)
                        try value.code(coder: superCoder)
						dictionary[key.stringValue as! Key] = value
					} else {
						let value = try container.decode(Value.self, forKey: key)
						dictionary[key.stringValue as! Key] = value
					}
				}
			} else {
				// We should have encoded as an array of alternating key-value pairs.
				var container = try coder.unkeyedContainer()
				let count = container.count
				guard count % 2 == 0 else {
					throw CodingError.dataCorrupted(CodingError.Context(codingPath: container.codingPath,debugDescription: "Expected collection of key-value pairs; encountered odd-length array instead."))
				}
				while !container.isAtEnd {
					let key = try container.decode(Key.self)
					guard !container.isAtEnd else {
						throw CodingError.dataCorrupted(CodingError.Context(codingPath: container.codingPath,debugDescription: "Unkeyed container reached end before value in key-value pair."))
					}
					if var value = self[key] {
                        let superCoder = try container.superCoder()
                        try value.code(coder: superCoder)
						dictionary[key] = value
					} else {
						let value = try container.decode(Value.self)
						dictionary[key] = value
					}
				}
			}

			self = dictionary
		}
	}
}

extension RawRepresentable where RawValue : Codable, Self : Codable {
	@inlinable
	public static func initObject(from coder: Coder) throws -> Self {
		let rawValue:RawValue = try coder.decode()
		guard let value = Self(rawValue: rawValue) else {
			throw CodingError.dataCorrupted(
				CodingError.Context(codingPath: coder.codingPath,debugDescription: "Cannot initialize \(Self.self) from invalid \(RawValue.self) value \(rawValue)"))
		}
		return value
	}
	@inlinable
	public mutating func code(coder: Coder) throws {
		if coder.isEncoding {
			var container = try coder.singleValueContainer()
			try container.encode(self.rawValue)
		} else {
            self = try Self.initObject(from: coder)
		}
	}
}

extension Bool : Codable {
    @inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension String : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Double : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Float : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Int : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Int8 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Int16 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Int32 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Int64 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension UInt : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension UInt8 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension UInt16 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension UInt32 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension UInt64 : Codable {
	@inlinable public static func initObject(from coder: Coder) throws -> Self { return try coder.decode() }
	@inlinable public mutating func code(coder: Coder) throws { try coder.codingBaseTypeValue(&self) }
}
extension Coder {
    public func codingBaseTypeValue <T> (_ l:inout T) throws where T:Codable {
        do{
            switch self.codingOperation {
            case .decode:
                //解码出nil或key不存在,或类型不符时,将抛error
                l = try self.decode()
            case .encode:
                try self.encode(l)
            }
        } catch {
            throw error
//            print("error:\(error)")
        }
    }
}
